package com.alibaba.citrus.cr.common.log;

import com.alibaba.citrus.cr.common.annotation.FunctionInvoker;
import com.alibaba.citrus.cr.common.exception.FunctionException;
import com.alibaba.citrus.cr.common.exception.FunctionSystemException;
import com.alibaba.cz.base.tool.log.Log;
import com.alibaba.cz.base.tool.message.ErrorCodeException;
import com.alibaba.fastjson.JSON;

import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;


/**
 * @author: qiaojiange
 * @create: 2021/10/02
 * @Description 日志工具类
 */

@Aspect
@Component
@Order(2)
public class FunctionInvokeHandler {

    @Pointcut("@annotation(com.alibaba.citrus.cr.common.annotation.FunctionInvoker)")
    private void cutMethod() {
    }

    @Around("cutMethod()")
    public Object doAroundAdvice(ProceedingJoinPoint proceedingJoinPoint) {
        long startTime = System.currentTimeMillis();
        Log log = Log.getLogger(AopUtils.getClazz(proceedingJoinPoint));
        Object result = null;
        String argsBody = null;
        try {
            argsBody = AopUtils.getArgsString(proceedingJoinPoint);
            result = proceedingJoinPoint.proceed();
        } catch (FunctionException e) {
            log.error(ErrorInfoUtils.buildErrorInfo(argsBody, proceedingJoinPoint, "Function", System.currentTimeMillis() - startTime), e);
            throw e;
        } catch (ErrorCodeException e) {
            throw e;
        } catch (Throwable e) {
            log.error(ErrorInfoUtils.buildErrorInfo(argsBody, proceedingJoinPoint, "Function", System.currentTimeMillis() - startTime), e);
            FunctionInvoker functionInvoker = AopUtils.getTargetMethodAnnotation(proceedingJoinPoint, FunctionInvoker.class);
            if (functionInvoker != null) {
                throw new FunctionSystemException(functionInvoker.errorCode(), e);
            }
        }

        AccessLogBody accessLogBody = result == null ? AccessLogBody.DEFAULT_ACCESS_LOG(argsBody) : convertResult(argsBody, result);
        FunctionInvoker functionInvoker = AopUtils.getTargetMethodAnnotation(proceedingJoinPoint, FunctionInvoker.class);
        boolean printLog = functionInvoker != null && functionInvoker.printLog();
        if (printLog) {
            log.info(buildAccessLog(proceedingJoinPoint, System.currentTimeMillis() - startTime, accessLogBody));
        }
        return result;
    }

    private String buildAccessLog(ProceedingJoinPoint point, long cost, AccessLogBody accessLogBody) {
        StringBuilder sb = new StringBuilder();
        FunctionInvoker functionInvoker = AopUtils.getTargetMethodAnnotation(point, FunctionInvoker.class);
        String tag = "-";
        if (hasRemark(functionInvoker)) {
            tag = StringUtils.isNotBlank(functionInvoker.remark()) ? functionInvoker.remark() : functionInvoker.value();
        }
        return ErrorInfoUtils.buildAccessLog(point, cost, accessLogBody, tag);
    }

    /**
     * 是否有标签
     *
     * @param facadeInvoker
     * @return
     */
    private boolean hasRemark(FunctionInvoker facadeInvoker) {
        return facadeInvoker != null && (StringUtils.isNotBlank(facadeInvoker.remark()) || StringUtils.isNotBlank(facadeInvoker.value()));
    }


    private AccessLogBody convertResult(String argsString, Object result) {

        return AccessLogBody.of(argsString, JSON.toJSONString(result), true, null, null);
    }

}
